Recursive Trigonometric Functions¶

In this notebook we shall see how we can compute the sine and cosine functions using recursion.

The root function computes the square root of a floating point number using binary search.

In [1]:
PI = 3.14159265359
full_angle = PI * 2
half_angle = PI / 2

# function to find x-th root of n
def root(n:float, x:int) -> float:
    
    # Max and min are used to take into account numbers less than 1
    lo = min(1, n)
    hi = max(1, n)
    mid = 0.0

    # Update the bounds to be off the target by a factor of 10
    while(100 * lo * lo < n):
        lo *= 10
    while(0.01 * hi * hi > n):
        hi *= 0.1

    for i in range(100):
        mid = (lo + hi)/2
        val = mid ** x
        if(val == n): return mid
        if(val > n): hi = mid
        else: lo = mid
    return mid

The range_check decorator normalises the input to fit between $-6.28$ and $6.28$

In [2]:
to_radians = lambda deg: ((deg % 360)/180.0) * PI
abs = lambda x: (x if x >= 0 else -x)

def range_check(func):

    def inner(*args):
        angle = (args[0] % full_angle) * (-1 if args[0] < 0 else 1)        
        return func(angle)
    
    return inner

The sine< and cosine function approximates the sine and cosine of a given angle

Modify the value of DELTA to adjust the accuracy.

Formulas used:¶

  • $\sin(2x) = 2\sin(x)\cos(x)$
  • $\cos(2x) = 2\cos^2(x) - 1$

Note:¶

  • Since we're finding then square root for computing sine, whether we take the positive or negative value depends on the quadrant of the input.
  • We don't need to check the quadrant for every recursion call.
In [3]:
DELTA = 0.001

@range_check
def cosine(x:float) -> float:
    if x == 0.0:
        return 1.0
    if abs(x) < DELTA:
        return x
    else:
        v = cosine(x / 2.0)
        return 2.0 * v * v - 1

@range_check
def rec_sine(x:float) -> float:
    if abs(x) < DELTA:
        return x
    else:
        v = rec_sine(x / 2.0)
        return 2.0 * v * root(1 - v * v, 2)

sine = lambda x: rec_sine(x) * (-1 if x <= full_angle and x >= PI else 1)

Driver code¶

In [4]:
if __name__ == "__main__":
    args = [0, 30, 45, 60, 90, 120, 270]
    for i in args:
        print(f"sin({i}) = {round(sine(to_radians(i)), 3)}")
        print(f"cos({i}) = {round(cosine(to_radians(i)), 3)}\n")
sin(0) = 0.0
cos(0) = 1.0

sin(30) = 0.5
cos(30) = 0.866

sin(45) = 0.707
cos(45) = 0.707

sin(60) = 0.866
cos(60) = 0.5

sin(90) = 1.0
cos(90) = -0.0

sin(120) = 0.866
cos(120) = -0.5

sin(270) = -1.0
cos(270) = 0.0